#ifndef GDT_HPP
#define GDT_HPP

#include <std/cstdint>

namespace gdt
{
struct [[gnu::packed]] GdtDescriptor
{
	uint16_t size;
	uint32_t address;
};
static_assert(sizeof(GdtDescriptor) == 6, "GdtDescriptor must be 6-bytes long.");
struct [[gnu::packed]] SegmentDescriptor
{
		uint16_t limit0;
		uint16_t base0;
		uint8_t base1;
		uint8_t flags0;
		uint8_t limit1AndFlags1;
		uint8_t base2;
};
static_assert(sizeof(SegmentDescriptor) == 8, "SegmentDescriptor must be 8-bytes long.");

namespace Flags0
{
enum Flags0 : uint8_t
{
	Accessed            = 1 << 0, Enabled             = 1 << 1,
	DirectionConforming = 1 << 2, DataOrCode          = 1 << 3,
	DescriptorType      = 1 << 4, Privilege1          = 1 << 5,
	Privilege2          = 1 << 6, Present             = 1 << 7
};
}

namespace Flags1
{
enum Flags1 : uint8_t
{
	AvailableForOs = 1 << 4,
	LongMode       = 1 << 5,
	Size           = 1 << 6,
	Granularity    = 1 << 7
};

}

alignas(sizeof(SegmentDescriptor)) [[gnu::section(".bootBss")]]
extern SegmentDescriptor globalDescriptorTable[3];

extern "C" [[gnu::section(".bootBss")]] gdt::GdtDescriptor gdtDescriptor;

[[gnu::section(".bootText")]] void initializeGdt();
[[gnu::section(".bootText")]]
SegmentDescriptor constructSegmentDescriptor(uint32_t base, uint32_t limit, uint8_t flags0, uint8_t flags1);

extern "C" [[gnu::section(".bootText")]] void flushGdt();

}
#endif